#pragma once
#include "Fourier.h"
#include "TwoObjectUpdater.h"
#include "Polynomial.h"
#include "CircleDivider.h"
#include <memory>
#include <string>

// forward declarations
struct IWICImagingFactory2;
struct ID2D1Factory;
struct IWICFormatConverter;
struct ID2D1HwndRenderTarget;
struct ID2D1Bitmap;
struct ID2D1SolidColorBrush;
struct ID2D1PathGeometry;
struct IDWriteTextFormat;

struct MagnifierData;

enum CircleAppearanceEnum {
	kCircleAppearanceGreenLine = 0,
	kCircleAppearanceSmoothColors,
	kCircleAppearanceDiscreteColors,
	kCircleAppearanceFewDiscreteColors
};


class ImageKeeper
{
	enum ChangeMode { kNoChange, kMove, kResize };

	ID2D1Factory*	mD2D1Factory;
	IWICFormatConverter* mFormatConverter;
	ID2D1HwndRenderTarget* mOriginalRenderTarget;
	ID2D1HwndRenderTarget* mMagnifierRenderTarget;
	ID2D1Bitmap* mOriginalBitmap;
	ID2D1Bitmap* mMagnifierBitmap;
	ID2D1Bitmap* mAnalyticsBitmap;
	ID2D1Bitmap* mCircleBitmap;
	ID2D1Bitmap* mLabelBitmap;
	ID2D1Bitmap* mSmoothStripeBitmap;
	ID2D1SolidColorBrush* mOriginalGreenBrush;
	ID2D1SolidColorBrush* mMagnifierGreenBrush;

	TwoObjectUpdater mMagnifierBitmapUpdater;
	TwoObjectUpdater mAnalyticsUpdater;
	TwoObjectUpdater mCircleUpdater;
	bool mMagnifierBitmapNeedsUpdate;
	bool mAnalyticsNeedsUpdate;
	bool mIsAnalyticsBitmapValid;
	bool mCircleNeedsUpdate;
	bool mIsCircleBitmapValid;

	bool mShowAnalytic;

	CircleAppearanceEnum mCircleAppearance;

	PolynomialCalculator mPolynomialCalculator;
	float mBufferForBaseFunctions[kSideOfMagnifierSquare][kMaxDegree + 1];
	int mDegree, mDegreeForWhichBaseFunctionsWereCalculated;

	FourierCalculator mFourierCalculator;
	CircleDivider mCircleDivider;
	CircleDivider mCircleDividerFew;

	float mCircleSegmentationThreshold;


	struct Pointf {double x, y; };
	Pointf mLastReflectingPoint;
	int mPixelCountPerMagnifierSide;

	HWND mMagnifierWindow;
	POINT mPreviousCentralPixel;

	Pointf mCenter;
	double mCircleRadius;
	Pointf mPrevMagnifierPoint;
	ChangeMode mModeAtClick;

	Pointf mDesktopDPIScale;

	int mNumberOfSimilarPolynomialCalculations;
	int mNumberOfSimilarCircleCalculations;
	LONGLONG mLastPolynomialCalculationTime;
	LONGLONG mLastCircleCalculationTime;
	LONGLONG mCumulativePolynomialCalculationTime;
	LONGLONG mCumulativeCircleCalculationTime;

	int mMaxIterationsForCircleDivision;
	int mCumulativeIterationsForCircleDivision;
	int mNumberOfCircleDivisions;

public:
	ImageKeeper(void);
	~ImageKeeper(void);

	bool CreateGraphicsFactories();
	bool RecreateOriginalRenderTarget(HWND);
	void ResizeOriginalRenderTarget(int cx, int cy);
	bool OpenFile(LPCTSTR filePath);

	SIZE GetSize() const;
	bool DrawOriginal(const POINT& scrollPosition, double scale,
		bool interpolate, bool drawCenter, const RECT& dirtyRect);
	RECT GetLastReflectingPointArea(const POINT& scrollPosition, double scale);

	void SetPixelCountPerMagnifierSide(int);
	bool DrawMagnifier(const RECT& dirtyRect);
	void SetDegree(int);
	void SetCircleAppearance(CircleAppearanceEnum);
	LPCTSTR GetMagnifierCursorShape(int x, int y) const;

	void SetReflectingPointFromDevicePixels(double x, double y);
	void GetReflectingPoint(double& outX, double& outY) const;
	void SetInitialPoint(double x, double y);
	void ChangeCircle(double newX, double newY);
	void ChangeCircleRadiusBy(double);
	void MoveCircleBy(double dx, double dy);
	bool IsCenterMoving() const;

	SIZE GetMagnifierSize() const;
	void SetMagnifierWindow(HWND);
	HWND GetMagnifierWindow();

	bool IsPointInMagnifierSquare(int x, int y) const;

	void ShowAnalytic(bool);
	void StartUpdatingAnalytics(int correspondingUpdateCountForBitmap);
	void StartUpdatingCircle();

	std::string GetPerformanceLog();

private:
	ChangeMode GetChangeMode(double x, double y) const;

	ID2D1SolidColorBrush* GetGreenBrush();
	ID2D1SolidColorBrush* GetMagnifierGreenBrush();
	bool RecreateMagnifierRenderTarget();

	IDWriteTextFormat* DefaultTextFormat();
	void DrawDiscreteColorsStripe(const CircleDivider&, int positionY);
	void DrawSegmentedCircle(const CircleDivider&);
	void DrawCircleInSolidColor(ID2D1SolidColorBrush*);

	void ClearPerformanceLog();

	void UpdateMagnifierBitmap();
	void UpdateAnalyticsBitmap();
	void UpdateCircleBitmap();
	void UpdateLabelBitmap();

	void StartUpdatingMagnifierBitmapData();
	void DoUpdateMagnifierBitmapData(std::shared_ptr<IWICFormatConverter>, POINT center,
		void* objectToUpdate);
	void MagnifierDataDidUpdate();

	void DoUpdateAnalytics(std::shared_ptr<MagnifierData>, int degree, void* objectToUpdate);
	void AnalyticsDidUpdate();

	void DoUpdateCircle(std::shared_ptr<Polynomial>, Pointf center, float radius,
		void* objectToUpdate);
	void CircleDataDidUpdate();
};

